package jmind.core.aspect;

/**
 * Created by weibo.xwb on 2017/9/19.
 */

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import jmind.base.annotation.CacheMonitor;
import jmind.base.cache.ICache;
import jmind.base.util.DataUtil;
import jmind.base.util.GlobalConstants;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentMap;

/**
 * @author wbxie
 * @CacheMonitor( key = "'user",exp=3600)
 * @CacheMonitor(name = "cacheName", prefix = "userid",key="#user.cid,#name",exp=3600)
 * <p>
 * key 用做前缀+ 方法参数值
 * @Component
 * @Aspect 好像什么时候都需要打这个标签
 * 2013-12-4
 * <aop:aspectj-autoproxy proxy-target-class="true" />
 * <aop:config expose-proxy="true"  />
 * @EnableAspectJAutoProxy(proxyTargetClass = true,exposeProxy = true)
 */
public abstract class AbstractCacheAspect {
    public abstract ICache getCache(ICache.Type type);

    final Logger logger = LoggerFactory.getLogger(getClass());
    final ExpressionParser parser = new SpelExpressionParser();
    final ConcurrentMap<String, List<Expression>> expressionMap = Maps.newConcurrentMap();


    private List<Expression> getExpressions(String spel) {
        List<Expression> list = expressionMap.get(spel);
        if (list == null) {
            list = Lists.newArrayList();
            StringTokenizer token = new StringTokenizer(spel, GlobalConstants.COMMA);
            while (token.hasMoreTokens()) {
                list.add(parser.parseExpression(token.nextToken()));
            }
            expressionMap.put(spel, list);
        }
        return list;

    }


    @Around(value = "execution(* *(..)) && @annotation(monitor)", argNames = "pjp,monitor")
    public Object doAround(ProceedingJoinPoint pjp, CacheMonitor monitor) throws Throwable {
        String key;
        if (monitor.key().isEmpty()) {
            key = DataUtil.composeKey(monitor.prefix(), pjp.getArgs());
        } else {
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            StandardEvaluationContext context = new StandardEvaluationContext();
            String[] names = signature.getParameterNames(); // 参数变量名
            Object[] values = pjp.getArgs(); // 参数值
            for (int i = 0; i < names.length; i++) {
                context.setVariable(names[i], values[i]);
            }

            StringBuilder sb = new StringBuilder(monitor.prefix());
            List<Expression> expressions = getExpressions(monitor.key());
            for (Expression exp : expressions) {
                sb.append(GlobalConstants.DASH).append(exp.getValue(context));
            }
            key = sb.toString();
        }

        logger.info("cachekey=" + key);

        ICache cache = getCache(monitor.type());
        //添加参数值 做为key
        if (monitor.remove()) {
            Object object = pjp.proceed();
            cache.delete(key);
            return object;
        }


        return cache.computeIfAbsent(key, monitor.exp(), k -> {
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                logger.error("", throwable);
                return null;
            }
        });


    }


}